In [1]:
include("dual.jl")
Out[1]:
Let's implement a very basic version of dual numbers (done much better here. A dual number is written as $$a + b\epsilon$$ where $a$ and $b$ are real numbers and $$\epsilon^2 = 0$$ Basically, a dual number encodes both a point in space and a local derivative.
To start, we'll assume that dual numbers are only a generalization of Float64
.
We will also define (though it's not rigorously true) that these numbers are subtypes of Real
. That way, any function that admits a Real
will also admit a Dual
.
Dual <: Real
for convenience, not math. We want Dual
s to go where Real
s are allowed.immutable
we could have used type
. immutable
means that we cannot change these numbers, but as a consequence, they can be stack-allocated and faster.Any
.) In contrast to function arguments, we absolutely want these to be concrete, if possible, since abstract fields lead to a performance hit.
In [2]:
aa = Dual(1, 2) # note implicit conversion to Float64
Out[2]:
In [3]:
fieldnames(aa)
Out[3]:
In [4]:
aa.val, aa.adj
Out[4]:
In [5]:
aa
Out[5]:
To make Dual
"just work" we need to define how to upconvert other numbers to it. We can do this by extending convert
.
Notes:
::
)Type{Dual}
; Type{T}
is a type of which the DataType T
is the only member (that is, we're specifying tha that the call looked like convert(Dual, 4.)
or similarx
can be any real numberDual
; Real numbers just have adj = 0
.
In [6]:
convert(Dual, 5)
Out[6]:
We also need to define a promotion rule. Promotion rules stipulate what happens when we need to find a type "big enough" to hold two things (for addition, comparison, etc.)
With promotion and conversion defined, we only need to add arithmetic for Dual
s. Other arithmetic operations automatically promote as needed.
In [7]:
Dual(5, 2) - Dual(3, 1)
Out[7]:
In [8]:
Dual(5, 2)/Dual(3, 1)
Out[8]:
Now watch what we get for free:
In [9]:
5 + Dual(3, 2), Dual(3, 2) + 2
Out[9]:
In [10]:
4.5/Dual(1, 1)
Out[10]:
In [11]:
Array{Dual}(rand(5, 5))
Out[11]:
In [12]:
A = rand(5, 5) + Dual(0, 1) * Array{Dual}(rand(5, 5))
Out[12]:
In [13]:
A * rand(5)
Out[13]:
In [14]:
minimum(A), maximum(A)
Out[14]:
In [15]:
Dual(1, 1) <= Dual(1, 0.8)
Out[15]:
In [16]:
A \ rand(5)
Out[16]:
In [17]:
function foo(x, y)
log(x + y) - y
end
Out[17]:
In [18]:
foo(Dual(1, 1), 2)
Out[18]:
In [19]:
foo(1, Dual(2, 1))
Out[19]:
In [ ]: